/*
 * SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
 * SPDX-License-Identifier: CC0-1.0
 */
/*  funb.fun同步更新
C 语言代码的头文件包含部分。其中包括了
标准输入输出头文件 <stdio.h>、
整数类型头文件 < inttypes.h>、SDK
配置头文件 "sdkconfig.h"、
FreeRTOS 的任务相关头文件 "freertos/FreeRTOS.h" 和 "freertos/task.h"、
ESP32 芯片信息头文件 "esp_chip_info.h"
以及 ESP32 闪存相关头文件 "esp_flash.h"。
这些头文件提供了不同的功能和接口，用于在程序中进行输入输出、处理特定整数类型、
获取芯片信息、进行闪存操作以及使用 FreeRTOS 进行多任务处理等。 */

#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
// 第二课 通用输入输出
#include "driver/gpio.h"

// 第二课 gpio_evt_queue 通用输入输出中断事件队列
// IRAM_ATTR内部存储器_属性
// gpio_isr_handler 通用输入输出中断服务
// gpio_num 通用输入输出引脚号的指针
static QueueHandle_t GPIO事件队列 = NULL;
static void IRAM_ATTR GPIO中断服务(void *参数)
{
    uint32_t GPIO引脚 = (uint32_t)参数;
    // xQueueSendFromISR 是 FreeRTOS 函数，用于在中断服务程序中向队列发送消息
    xQueueSendFromISR(GPIO事件队列, &GPIO引脚, NULL);
}

/**
 * @brief GPIO任务示例函数
 *
 * 该函数用于处理GPIO中断事件队列中的消息，并打印GPIO引脚的中断状态和电平值。
 *
 * @param arg 传递给任务的参数，这里未使用
 */
static void GPIO任务示例(void *参数)
{
    uint32_t 引脚号; // 用于存储接收到的GPIO引脚号
    for (;;)
    { // 无限循环，不断处理队列中的消息
        //  FreeRTOS 的 xQueueReceive 函数来从一个队列中接收消息，从gpio_evt_queue队列中接收消息，阻塞直到有消息可用
        if (xQueueReceive(GPIO事件队列, &引脚号, portMAX_DELAY))
        {
            // 打印GPIO引脚的中断信息和当前电平值， ESP-IDF 提供了 gpio_get_level 函数来获取 GPIO 引脚的电平值
            printf("GPIO引脚号[%" PRIu32 "] intr中断, val电平值: %d\n", 引脚号, gpio_get_level(引脚号));

            // 第一课 重启
            for (int i = 10; i >= 0; i--)
            {
                printf("在 % d 秒内重新启动…\n", i);
                // vTaskDelay 是 FreeRTOS 函数，用于在任务中进行延迟，portTICK_PERIOD_MS单位是毫秒
                vTaskDelay(1000 / portTICK_PERIOD_MS);
            }
            printf("现在重启.\n");
            // fflush 函数用于刷新输出缓冲区，确保所有输出都被写入到输出设备中
            fflush(stdout);
            // esp_restart 函数用于重启 ESP32 芯片，在 esp_restart 函数之后的代码将不会被执行
            esp_restart();
            // 第一课 重启
        }
    }
}

void app_main(void)
{
    printf("Hello SoPlus! 你好索加! \n");
    /* 打印芯片信息
    esp_chip_info_t 类型名，后接的chip_info可以改名字。
    cores 核心数，

    */
    esp_chip_info_t 芯片信息;
    uint32_t flash_size;
    esp_chip_info(&芯片信息);
    printf("这是 %s 芯片内含 %d CPU 核心, %s%s%s%s, ",
           CONFIG_IDF_TARGET,
           芯片信息.cores,
           (芯片信息.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "",
           (芯片信息.features & CHIP_FEATURE_BT) ? "BT" : "",
           (芯片信息.features & CHIP_FEATURE_BLE) ? "BLE" : "",
           (芯片信息.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4 (Zigbee/Thread)" : "");

    unsigned major_rev = 芯片信息.revision / 100;
    unsigned minor_rev = 芯片信息.revision % 100;
    printf("“硅版本修订 v%d.%d, ", major_rev, minor_rev);
    if (esp_flash_get_size(NULL, &flash_size) != ESP_OK)
    {
        printf("获取闪存大小失败");
        return;
    }

    printf("%" PRIu32 "MB %s flash\n", flash_size / (uint32_t)(1024 * 1024),
           (芯片信息.features & CHIP_FEATURE_EMB_FLASH) ? "内置" : "外接");

    printf("堆内存的最小可用大小: %" PRIu32 " 字节\n", esp_get_minimum_free_heap_size());

    /*     for (int i = 10; i >= 0; i--) {
            printf("在 % d 秒内重新启动…\n", i);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
        printf("现在重启.\n");
        fflush(stdout);
        esp_restart(); */

    // 零初始化配置结构
    gpio_config_t io_conf = {
        // GPIO_INTR_NEGEDGE 是 ESP-IDF 中定义的一个宏，用于设置 GPIO 引脚的中断类型为下降沿触发
        .intr_type = GPIO_INTR_NEGEDGE,
        // GPIO_MODE_INPUT 是 ESP-IDF 中定义的一个宏，用于设置 GPIO 引脚的模式为输入模式
        .mode = GPIO_MODE_INPUT,
        // 1ULL 是一个无符号长整型常量，表示 1 左移 9 位，即 2 的 9 次方。
        // 1ULL << GPIO_NUM_9 表示将 1 左移 GPIO_NUM_9 位，即 GPIO_NUM_9 对应的 GPIO 引脚的位掩码
        .pin_bit_mask = 1ULL << GPIO_NUM_9,
        // 0 表示禁用下拉电阻，1 表示启用下拉电阻
        .pull_down_en = 0,
        .pull_up_en = 1};
    //  ESP-IDF 使用 gpio_config 给定的设置配置 GPIO
    gpio_config(&io_conf);

    // xQueueCreate 是 FreeRTOS 函数，用于创建一个队列
    GPIO事件队列 = xQueueCreate(10, sizeof(uint32_t));

    // xTaskCreate 是 FreeRTOS 函数，用于创建一个任务
    xTaskCreate(GPIO任务示例, "GPIO任务示例", 2048, NULL, 10, NULL);

    // gpio_install_isr_service 是 ESP-IDF 中用于安装 GPIO 中断服务程序的函数
    gpio_install_isr_service(0);

    // gpio_isr_handler_add 是 ESP-IDF 中用于将中断服务程序与特定的 GPIO 引脚关联的函数
    gpio_isr_handler_add(GPIO_NUM_9, GPIO中断服务, (void *)GPIO_NUM_9);
}
